<?php
/**
 * Application.php
 *
 * @category 	Madoqua
 * @package 	Application
 */

/** Front Controller */
require_once 'Zend/Controller/Front.php';

/** Zend Layout */
require_once 'Zend/Layout.php';

/** Zend Config XML */
require_once 'Zend/Config/Xml.php';

/** config file loader with caching */
require_once 'Madoqua/Cache/Config.php';

/** error handling plugin */
require_once 'Zend/Controller/Plugin/ErrorHandler.php';

/** layout plugin */
require_once 'Madoqua/Controller/Plugin/Layout.php';

/** access control plugin */
require_once 'Madoqua/Controller/Plugin/Access.php';

/** action cache plugin */
require_once 'Madoqua/Controller/Plugin/ActionCache.php';

/** Madoqua's viewrenderer */
require_once 'Madoqua/Controller/Action/Helper/ViewRenderer.php';

/** ZDT */
require_once 'Zend/Db/Table.php';

/** mime-type headers plugin */
require_once 'Madoqua/Controller/Plugin/Headers.php';

/** sessions */
require_once 'Zend/Session.php';

/**
 * Madoqua_Application
 *
 * all application related stuff. Most of this should be separated into different components,
 * such as a custom FC and a custom DB.
 *
 * @category 	Madoqua
 * @package 	Application
 * @todo read this phpdoc-block and fix it =]
 */
class Madoqua_Application
{
    /**
     * the application directory
     *
     * @var string
     */
    protected static $_applicationDirectory;

    /**
     * "root" directory
     *
     * @var string
     */
    protected static $_rootDirectory;

    /**
     * the configuration
     *
     * @var Zend_Config_Xml
     */
    protected static $_configuration;

    /**
     * database adapter
     *
     * @var Zend_Db_Adapter_Abstract
     */
    protected static $_database;

    /**
     * session namespace
     *
     * @var Zend_Session_Namespace
     */
    protected static $_session;

    /**
     * modules that are just for admins, this is part of the design. Madoqua does not have
     * an ACL-based approach for security. Instead we have binary exclusion of admins and
     * non-admins. KISS ftw.
     *
     * @var array
     */
    protected static $_adminModules = array('admin', 'blogAdmin');

    /**
     * figure out whether the application has been installed
     *
     * @return bool
     */
    public static function isInstalled() {
        return file_exists(self::getApplicationDirectory(). DIRECTORY_SEPARATOR  . 'config/installed');
        //at the end of the installation, the installer writes a file called
        //"installed" to the root of the data directory.
    }

    /**
     * get a version number for this installation
     *
     * @return string
     */
    public static function getVersion() {
        return '0.1';
        //yes... this is... very very static :D
    }

    /**
     * set the application directory
     *
     * @param string $applicationDirectory
     * @return void
     */
    public static function setApplicationDirectory($applicationDirectory)
    {
        self::$_applicationDirectory = $applicationDirectory;
    }

    /**
     * get the application directory
     *
     * @return string
     */
    public static function getApplicationDirectory() {
        if (empty(self::$_applicationDirectory)) {
            throw new Exception('Application directory not set');
        }
        return self::$_applicationDirectory;
    }

    /**
     * set the "root" directory
     *
     * @param $rootDirectory
     * @return void
     */
    public static function setRootDirectory($rootDirectory)
    {
        self::$_rootDirectory = $rootDirectory;
    }

    /**
     * get the root directory
     *
     * @return string
     */
    public static function getRootDirectory()
    {
        return self::$_rootDirectory;
    }

    /**
     * get the temp directory
     *
     * @return string
     */
    public static function getTempDirectory()
    {
        return self::$_rootDirectory .  DIRECTORY_SEPARATOR . 'temp';
    }

    /**
     * get the Data directory
     *
     * @return unknown
     */
    public static function getDataDirectory()
    {
        return self::$_rootDirectory . DIRECTORY_SEPARATOR . 'data';
    }

    /**
     * get the application's configuration
     *
     * @return Zend_Config_Xml|bool
     */
    public static function getConfiguration($reload = false) {
        if (empty(self::$_configuration) || $reload) {
            try {
                $file = self::getApplicationDirectory() . DIRECTORY_SEPARATOR . 'config/madoqua.xml';
                //var_dump($file);
                //the config file to be loaded
                self::$_configuration = Madoqua_Cache_Config::get($file);
                //the main configuration for the application
            } catch (Exception $e) {
                self::$_configuration = false;
            }
        }
        return self::$_configuration;
    }

    /**
     * get the database
     *
     * with any luck, this method won't get called during execution. Go go caching!
     *
     * also, go go dik-diks.
     *
     * @return Zend_Db_Adapter_Abstract
     */
    public static function getDatabase() {
        if (empty(self::$_database)) {
            $config = self::getConfiguration()->database;

            if (empty($config->adapter) || empty($config->dbname)) {
                return null;
            }
            //don't return a db if none is configured

            if ($config->adapter == 'pdo_sqlite') {
                $adapter =  Zend_Db::factory($config->adapter, array(
                    'dbname' => self::getDataDirectory() . DIRECTORY_SEPARATOR . 'database/db.sl3'
                ));
            } else {
                $adapter = Zend_Db::factory($config->adapter, array(
                    'host' => $config->host,
                    'username' => $config->username,
                    'password' => $config->password,
                    'dbname' => $config->dbname
                ));
                //set up adapter
            }

            // Add firebug logging if set in config
            if (isset(self::$_configuration->environment)) {

                $fireConfig = self::getConfiguration()->environment->logging->fireBug;

                if (self::inDevelopment() && $fireConfig->enabled && $fireConfig->log_db_queries) {
                    require_once 'Zend/Db/Profiler/Firebug.php';
                    $profiler = new Zend_Db_Profiler_Firebug('All DB Queries');
                    $profiler->setEnabled(true);
                    $adapter->setProfiler($profiler);
                }
            }

            if ($config->adapter == 'pdo_mysql') {
                $adapter->query('SET NAMES utf8;');
            }
            //FIXME make abstract/dynamic

            self::$_database = $adapter;
        }

        return self::$_database;
    }

    /**
     * get the session namespace
     *
     * @return Zend_Session_Namespace
     */
    public static function getSession()
    {
        if (empty(self::$_session)) {
            self::$_session = new Zend_Session_Namespace('madoqua');
        }
        return self::$_session;
    }

    /**
     * get a list of modules that are for admins only
     *
     * @return array
     */
    public static function getAdminModules()
    {
        if (self::isInstalled() && !in_array('install', self::$_adminModules)) {
            self::$_adminModules[] = 'install';
        }
        //add installer to admin modules after installation

        return self::$_adminModules;
    }

    /**
     * get the front controller
     *
     * @return Zend_Controller_Front
     */
    public static function getFront() {
        return Zend_Controller_Front::getInstance();
    }

    /**
     * start MVC
     *
     * @return void
     */
    public static function startMvc()
    {
        Zend_Session::start(array(
            'save_path' => self::getDataDirectory() . DIRECTORY_SEPARATOR . 'session',
            'use_only_cookies' => true
        ));
        //start the session

        Zend_Db_Table::setDefaultAdapter(self::getDatabase());
        //set up default adapter for ZDT

        $frontController = self::getFront();
        //fc instance

        $frontController->addModuleDirectory(self::getApplicationDirectory() . DIRECTORY_SEPARATOR . 'modules/');
        //all our modules are stored under application/modules

        $frontController->setParam('prefixDefaultModule', true);
        //also prefix default module (which imho should be default behaviour)
        $frontController->setDefaultModule('front');
        //make front the default module

        Zend_Controller_Action_HelperBroker::addPath('Madoqua/Controller/Action/Helper', 'Madoqua_Controller_Action_Helper');
        Zend_Controller_Action_HelperBroker::addPath('Naneau/Controller/Action/Helper', 'Naneau_Controller_Action_Helper');
        //additional action helper paths

        self::addPlugins();
        //add plugins to the front controller

        self::startLayout();
        //start the layout

        $routesFile = self::getApplicationDirectory() . DIRECTORY_SEPARATOR . 'config/routes.xml';
        //the config file to be loaded for the router
        $routeConfig = Madoqua_Cache_Config::get($routesFile);
        //router configuration
        $frontController->getRouter()->addconfig($routeConfig, 'routes');
        $frontController->getRouter()->addDefaultRoutes();
        //set up the router

        $frontController->setBaseUrl(self::getConfiguration()->rewriting->base);
        //base url

        $frontController->throwExceptions(self::$_configuration->environment->throw_exceptions ? true : false);

        // Quick kludge to allow testing controllers with the test case
        // variants of the resposne object.
        if(!defined('MADOQUA_TESTS_DIR')) {
            $frontController->dispatch();
        }
        //dispatch! go go go!
    }

    /**
     * start layout
     *
     * @return void
     */
    protected static function startLayout() {
        $layoutPlugin = new Madoqua_Controller_Plugin_Layout();
        self::getFront()->registerPlugin($layoutPlugin);
        //the layout plugin (will set the layout for front/admin)

        $viewRenderer = new Madoqua_Controller_Action_Helper_ViewRenderer();
        Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
        $viewRenderer->initView();
        $viewRenderer->view->doctype('XHTML1_STRICT');
        $viewRenderer->view->setEncoding('utf-8');
        //use custom view renderer for themes

        $viewRenderer->view->addHelperPath(self::getApplicationDirectory() . DIRECTORY_SEPARATOR .  'layouts/helpers/', 'Madoqua_View_Helper');
        $viewRenderer->view->addHelperPath('Madoqua/View/Helper/', 'Madoqua_View_Helper');
        $viewRenderer->view->addHelperPath('Naneau/View/Helper/', 'Naneau_View_Helper');
        //set up view object with proper helper paths
    }

    /**
     * add plugins to the FC
     *
     * @return void
     */
    protected static function addPlugins()
    {
        $frontController = self::getFront();
        //fc instance

        $errorHandler = new Zend_Controller_Plugin_ErrorHandler();
        $errorHandler->setErrorHandlerModule('error');
        $errorHandler->setErrorHandlerController('error');
        $errorHandler->setErrorHandlerAction('error');
        $frontController->registerPlugin($errorHandler);
        //error handler

        $accessPlugin = new Madoqua_Controller_Plugin_Access();
        $frontController->registerPlugin($accessPlugin);
        //access plugin

        $headersPlugin = new Madoqua_Controller_Plugin_Headers();
        $frontController->registerPlugin($headersPlugin);
        //headers plugin
    }

    /**
     * get a list of modules currently available
     *
     * @return array
     */
    public static function getModules() {
        return array_keys(self::getFront()->getControllerDirectory());
    }

    /**
     * Evaluate application state
     * @return bool
     */
    public static function inDevelopment() {
        if (!isset(self::$_configuration->environment)) {
            return false;
        }
        return (self::$_configuration->environment->development) ? true  : false;
    }
}